package com.bazaaroid.server.gae.core.dao.partner.impl;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import au.com.bytecode.opencsv.CSVReader;

import com.bazaaroid.server.gae.core.dao.impl.JDOGenericDAOImpl;
import com.bazaaroid.server.gae.core.dao.partner.DiscountDAO;
import com.bazaaroid.server.gae.core.dao.partner.PartnerDAO;
import com.bazaaroid.server.gae.core.dao.partner.ProductDAO;
import com.bazaaroid.server.gae.core.enums.Languages;
import com.bazaaroid.server.gae.core.ioc.GuiceFactory;
import com.bazaaroid.server.gae.core.persistence.model.partner.Discount;
import com.bazaaroid.server.gae.core.persistence.model.partner.Partner;
import com.bazaaroid.server.gae.core.persistence.model.partner.Product;
import com.bazaaroid.server.gae.core.persistence.model.partner.Discount.TimePeriod;
import com.bazaaroid.server.gae.util.DateUtil;
import com.bazaaroid.server.gae.util.NumberUtil;
import com.google.appengine.api.datastore.Key;

public class JDODiscountDAOImpl extends JDOGenericDAOImpl<Discount, Key>
		implements DiscountDAO {

	public Discount getDiscountByProduct(Product product) {
		PersistenceManager persistenceManager = getPersistenceManager();
		persistenceManager.setDetachAllOnCommit(true);
		Discount discount = null;

		Query query = persistenceManager.newQuery(getEntityClass());
		query.declareImports("import com.google.appengine.api.datastore.Key;");
		query.setFilter("this.product == productKeyParam");
		query.declareParameters("Key productKeyParam");
		query.setUnique(true);

		try {
			discount = (Discount) query.execute(product.getKey());
		}
		finally {
			query.closeAll();
			persistenceManager.close();
		}

		return discount;
	}

	@SuppressWarnings("unchecked")
	public List<Key> findDiscountsStartBefore(Date dateFrom) {
		PersistenceManager persistenceManager = getPersistenceManager();
		persistenceManager.setDetachAllOnCommit(true);
		List<Key> discounts = new ArrayList<Key>();

		Query query = persistenceManager.newQuery(getEntityClass());
		query.setResult("key");
		query.declareImports("import java.util.Date;");
		query.setFilter("timePeriod.dateFrom <= dateFromParam");
		query.declareParameters("Date dateFromParam");

		try {
			discounts = (List<Key>) query.execute(dateFrom);
			discounts.size();
		}
		finally {
			query.closeAll();
			persistenceManager.close();
		}

		return discounts;
	}

	@SuppressWarnings("unchecked")
	public List<Key> findDiscountsEndAfter(Date dateTo) {
		PersistenceManager persistenceManager = getPersistenceManager();
		persistenceManager.setDetachAllOnCommit(true);
		List<Key> discounts = new ArrayList<Key>();

		Query query = persistenceManager.newQuery(getEntityClass());
		query.setResult("key");
		query.declareImports("import java.util.Date;");
		query.setFilter("timePeriod.dateTo >= dateToParam");
		query.declareParameters("Date dateToParam");

		try {
			discounts = (List<Key>) query.execute(dateTo);
			discounts.size();
		}
		finally {
			query.closeAll();
			persistenceManager.close();
		}

		return discounts;
	}

	public static Comparator<Discount> dateComparator = new Comparator<Discount>() {
		public int compare(Discount discount1, Discount discount2) {
			Date dateFrom1, dateTo1, dateFrom2, dateTo2;
			if(discount1.getTimePeriod() != null) {
				dateFrom1 = discount1.getTimePeriod().getDateFrom();
				dateTo1 = discount1.getTimePeriod().getDateTo();
			} else {
				dateFrom1 = null;
				dateTo1 = null;
			}
			if(discount2.getTimePeriod() != null) {
				dateFrom2 = discount2.getTimePeriod().getDateFrom();
				dateTo2 = discount2.getTimePeriod().getDateTo();
			} else {
				dateFrom2 = null;
				dateTo2 = null;
			}

			int cmp = DateUtil.compareSafeDesc(dateFrom1, dateFrom2);

			if(cmp == 0)
				cmp = DateUtil.compareSafeDesc(dateTo1, dateTo2);

			return cmp;
		}
	};

	public List<Discount> findActiveDiscounts() {
		Date today = new Date();
		List<Key> discountKeys1 = findDiscountsStartBefore(today);
		List<Key> discountKeys2 = findDiscountsEndAfter(today);

		// Can't modify query results.
		// To find intersection of query results we must make new collections.
		Set<Key> results1 = new HashSet<Key>(), results2 = new HashSet<Key>();
		results1.addAll(discountKeys1);
		results2.addAll(discountKeys2);
		results1.retainAll(results2);

		List<Discount> discounts = results1.isEmpty()
			? new ArrayList<Discount>()
			: getByKeys(results1);

		Collections.sort(discounts, dateComparator);
		return discounts;
	}

	@Override
	public void importDiscountsFromFile(String filePath) {
		PersistenceManager persistenceManager = getPersistenceManager();
		CSVReader csvReader = null;

		try {
			InputStreamReader inputStreamReader = new InputStreamReader(
					new BufferedInputStream(new FileInputStream(filePath)), "UTF-8");

			csvReader = new CSVReader(inputStreamReader, ';', '\"', 2);

			Date today = new Date();
			String [] nextLine;

			PartnerDAO partnerDAO = GuiceFactory.getInjector()
				.getInstance(PartnerDAO.class);
			ProductDAO productDAO = GuiceFactory.getInjector()
				.getInstance(ProductDAO.class);

			while ((nextLine = csvReader.readNext()) != null) {
				int index = 0;

				String partnerName = nextLine[index++].trim();
				String productLocalId = nextLine[index++].trim();
				String name = nextLine[index++].trim();
				String description = nextLine[index++].trim();
				String percentageStr = nextLine[index++].trim();
				String dateFromStr = nextLine[index++].trim();
				String dateToStr = nextLine[index++].trim();
				String language = nextLine[index++].trim();

				Partner partner = partnerDAO.getPartnerByName(partnerName);
				Product product = partner != null
					? productDAO.getProductByPartnerAndLocalId(partner, productLocalId)
					: null;

				if(partner != null && product != null) {
					Discount discount = getDiscountByProduct(product);

					if(discount == null) {
						discount = new Discount();
						discount.setCreated(today);
				   	}

					if(Languages.isSupportedLanguage(language))
						discount.setLanguage(language);
					else
						discount.setLanguage(null);

					discount.setName(name);
					discount.setDescription(description);
					discount.setModified(today);
					discount.setPercentage(NumberUtil.toInt(percentageStr));
					discount.setPriceWithDiscount(product.getPrice() -
							(product.getPrice() * discount.getPercentage()) / 100.0);

					TimePeriod timePeriod = new TimePeriod();
					timePeriod.setDateFrom(DateUtil.stringToDate(dateFromStr));
					timePeriod.setDateTo(DateUtil.stringToDate(dateToStr));
					discount.setTimePeriod(timePeriod);

					discount.setProduct(product.getKey());
					makePersistent(discount);
					persistenceManager.close();
					persistenceManager = getPersistenceManager();

					product = persistenceManager.getObjectById(Product.class, product.getKey());
					product.setDiscount(discount.getKey());
					persistenceManager.makePersistent(product);
				}
			}
			csvReader.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			persistenceManager.close();
		}
	}

}
